fix: use exported name for namespaced DI token references#375
fix: use exported name for namespaced DI token references#375brandonroberts wants to merge 1 commit into
Conversation
Aliased imports used as DI tokens emitted a namespace member access keyed by the
local binding instead of the module's exported name. For
`import { Foo as Bar } from "./m"` used as a constructor dependency, the compiler
generated `i1.Bar` even though `m` only exports `Foo`, so the reference resolved to
`undefined` at runtime and broke injection (the bundler also flags it as a missing
export).
Both namespaced-DI-reference paths used the local name:
- factory deps and host directives in component/transform.rs
(resolve_factory_dep_namespaces, resolve_host_directive_namespaces)
- the component constructor-dep path in component/dependency.rs
(create_token_expression)
Carry the exported name through to both: transform.rs already had `imported_name`
on its import info; dependency.rs gains a `token_imported_name` field populated from
the import map in component/decorator.rs. A namespace member access now uses the
exported name, falling back to the local name when they match.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@codex[agent] review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: dd0a02f393
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| d.token_source_module = Some(import_info.source_module.clone()); | ||
| // Carry the exported name so namespaced refs (`i1.X`) use the export | ||
| // name, not the local alias, for aliased imports. | ||
| d.token_imported_name = import_info.imported_name.clone(); |
There was a problem hiding this comment.
Use exported names in ctor metadata too
When emit_class_metadata is enabled, which is the default, these same component constructor_deps are later passed to build_ctor_params_metadata; that path still uses the local token/type name for namespace property access (class_metadata/builders.rs:778 and :822). For import { Foo as Bar } ... constructor(x: Bar), the factory now emits i1.Foo, but ɵsetClassMetadata still emits ctorParameters: () => [{ type: i1.Bar }], so dev/TestBed metadata and bundlers still see the missing export for the exact aliased DI case this patch is addressing.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
To use Codex here, create an environment for this repo.
There was a problem hiding this comment.
To use Codex here, create an environment for this repo.
Summary
Aliased imports used as DI tokens emitted a namespace member access keyed by the local binding instead of the module's exported name. For:
the compiler generated
i1.Bareven thoughmonly exportsFoo, so the reference resolved toundefinedat runtime — breaking Angular dependency injection (and bundlers flag it as a missing export, e.g. rolldownIMPORT_IS_UNDEFINED).A namespace member access (
i1.X) must always use the module's exported name, never the importer's local alias.Root cause
Both code paths that build namespaced DI references used the local name:
component/transform.rs—resolve_factory_dep_namespaces(factory/constructor deps) andresolve_host_directive_namespaces(host directives). These already hadimported_nameavailable on the import info but used the localname.component/dependency.rs—create_token_expression(component constructor-dep path). ItsR3DependencyMetadataonly carried the local token name.Fix
transform.rs: useimport_info.imported_name, falling back to the local name when they match.dependency.rs: add atoken_imported_namefield to the componentR3DependencyMetadata, populated from the import map incomponent/decorator.rs, and use it when building the namespace property access.Test plan
component::dependency::tests::test_aliased_import_uses_exported_name) asserting the emitted reference uses the exported name (i1.ExportedName), not the local alias.cargo test -p oxc_angular_compiler— 2356 passed, 0 failed.cargo fmt -- --checkclean.import { X as XAbstraction }convention pervasively):IMPORT_IS_UNDEFINEDwarnings went 9 → 0 and the app now bootstraps with zero runtime errors (previously a hardCannot read properties of undefinedcrash).Notes
Found via that Bitwarden build. Separately, that app surfaced a distinct, pre-existing gap (not addressed here): non-standalone (
@NgModule-declared) components are emitted with no directivedependencies/scope, so directives likeNgIfdon't match at runtime. That's a larger NgModule-compilation-scope feature and is out of scope for this fix.🤖 Generated with Claude Code